<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Point lighted cube (with animation)</title>
    <script src="lib/webgl-utils.js"></script>
    <script src="lib/webgl-debug.js"></script>
    <script src="lib/cuon-utils.js"></script>
    <script src="lib/cuon-matrix.js"></script>
    <style>
        body {
            border: 0;
            background-color: white;
        }
    </style>
</head>

<body onload="main()">
<canvas id="webgl" style="width:1000px;height: 1000px;">
    Please use a browser that supports "canvas"
</canvas>
</body>
<script>
    // 多个立方体  不旋转
    var VSHADER_SOURCE =`
attribute vec4 a_Position;
attribute vec4 a_VertexPosition;
attribute vec4 a_Color;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_ModelMatrix;
uniform mat4 u_NormalMatrix;
uniform vec3 u_LightColor;
uniform vec3 u_LightPosition;
uniform vec3 u_AmbientLight;
varying vec4 v_Color;
//着色器程序针对每一个顶点执行
void main() {
  //计算点的位置
  gl_Position = u_MvpMatrix * a_Position;

  //物体的运动会导致表面的法向量方向发生变化，求出每个法向量在运动后的新值
  vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));

  // 单个点的位置
  vec4 vertexPosition = u_ModelMatrix * a_Position;

  //光线方向
  vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));

  //法向量和光线方向的点积
  float nDotL = max(dot(lightDirection, normal), 0.0);

  //反射光
  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;

  // 环境光
  vec3 ambient = u_AmbientLight * a_Color.rgb;

  // 表面颜色
  v_Color = vec4(diffuse + ambient, a_Color.a);
}
`;

    var FSHADER_SOURCE =`
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}`;

    var VSHADER_SOURCE2=`
attribute vec4 a_VertexPosition;
uniform mat4 u_MvpMatrix2;
void main(){
gl_Position=u_MvpMatrix2 * a_VertexPosition;
}
`;
    var FSHADER_SOURCE2=`
void main(){
gl_FragColor=vec4(1.0,1.0,1.0,1.0);
}
`;

    var vertices,normals,indices,pointArray=[],translateCube,canvas,n;
    function main() {

        canvas = document.getElementById('webgl');
        // 获取浏览器中画布的显示尺寸
        var displayWidth  = canvas.clientWidth;
        var displayHeight = canvas.clientHeight;
        if (canvas.width  !== displayWidth || canvas.height !== displayHeight) {
            canvas.width  = displayWidth;
            canvas.height = displayHeight;
        }


        var gl=getWebGLContext(canvas);
        // var gl = canvas.getContext('webgl',
        //     { antialias: true });
        if(!gl){
            console.log('无法获取webGL绘图环境');
            return;
        }
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);


        var surfaceProgram=createProgram(gl,VSHADER_SOURCE,FSHADER_SOURCE);
        var edgeProgram=createProgram(gl,VSHADER_SOURCE2,FSHADER_SOURCE2);
        if(!surfaceProgram||!edgeProgram){
            console.log('创建程序对象失败');
            return;
        }

        gl.enable(gl.DEPTH_TEST);
        for(var i=0;i<1000;i++){
            drawCube(gl,surfaceProgram,i)
            drawLine(gl,edgeProgram);
        }

    }

    function drawCube(gl,surfaceProgram,i){
        gl.useProgram(surfaceProgram);

        // if(n==undefined){
        n = initVertexBuffers(gl,surfaceProgram);
        if (n < 0) {
            console.log('Failed to set the vertex information');
            return;
        }
        // }


        // Get the storage locations of uniform variables and so on
        var u_ModelMatrix = gl.getUniformLocation(surfaceProgram, 'u_ModelMatrix');
        var u_MvpMatrix = gl.getUniformLocation(surfaceProgram, 'u_MvpMatrix');
        var u_NormalMatrix = gl.getUniformLocation(surfaceProgram, 'u_NormalMatrix');
        var u_LightColor = gl.getUniformLocation(surfaceProgram, 'u_LightColor');
        var u_LightPosition = gl.getUniformLocation(surfaceProgram, 'u_LightPosition');
        var u_AmbientLight = gl.getUniformLocation(surfaceProgram, 'u_AmbientLight');
        if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition　|| !u_AmbientLight) {
            console.log('Failed to get the storage location');
            return;
        }

        gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
        gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);
        gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
        var modelMatrix = new Matrix4();  // Model matrix
        var mvpMatrix = new Matrix4();    // Model view projection matrix
        var normalMatrix = new Matrix4(); // Transformation matrix for normals
        modelMatrix.setRotate(90, 0, 1, 0); // Rotate around the y-axis

        translateCube={
            x:i-3,
            y:0,
            z:0
        };
        modelMatrix.setTranslate(translateCube.x,translateCube.y,translateCube.z);
        gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

        mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
        mvpMatrix.lookAt(16, 6, 20, 30, 0, 0, 0, 1, 0);
        mvpMatrix.multiply(modelMatrix);
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

        normalMatrix.setInverseOf(modelMatrix);
        normalMatrix.transpose();
        gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

    }

    function drawLine(gl,edgeProgram){
        gl.useProgram(edgeProgram);
        //画edge先的部分
        getPointsArray();
        if(!initArrayBuffer(gl,'a_VertexPosition', new Float32Array(pointArray), 3, gl.FLOAT,edgeProgram)) return -1;
        var u_MvpMatrix2 = gl.getUniformLocation(edgeProgram, 'u_MvpMatrix2');
        if(!u_MvpMatrix2){
            console.log('Failed to get the storage location');
            return;
        }
        var modelMatrix2 = new Matrix4();  // Model matrix
        var mvpMatrix2 = new Matrix4();    // Model view projection matrix
        modelMatrix2.setTranslate(translateCube.x,translateCube.y,translateCube.z);

        mvpMatrix2.setPerspective(30, canvas.width/canvas.height, 1, 100);
        mvpMatrix2.lookAt(16, 6, 20, 30, 0, 0, 0, 1, 0);
        mvpMatrix2.multiply(modelMatrix2);
        gl.uniformMatrix4fv(u_MvpMatrix2, false, mvpMatrix2.elements);
        gl.drawArrays(gl.LINES,0,pointArray.length/3);

    }
    function getPointsArray(){
        var edge = [ 0, 0 ], edges = {}, edge1, edge2;
        var key, keys = [ 'a', 'b', 'c' ];
        for(var i=0,l=indices.length;i<l;i=i+3){
            var face={a:indices[i],b:indices[i+1],c:indices[i+2]};

            var normal=[normals[i],normals[i+1],normals[i+2]];

            for(var j=0;j<3;j++){
                edge1=face[keys[j]];
                edge2=face[keys[(j+1)%3]];
                edge[ 0 ] = Math.min( edge1, edge2 );
                edge[ 1 ] = Math.max( edge1, edge2 );
                key = edge[ 0 ] + ',' + edge[ 1 ];
                if ( edges[ key ] === undefined ) {
                    edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1:face , face2: undefined };
                } else {
                    edges[ key ].face2 = face;
                }
            }
            // console.log(edges)
        }

        pointArray=[];
        for(key in edges){
            var e=edges[key];
            if(e.face2 === undefined) {
                pointArray.push(vertices[e.index1*3],vertices[e.index1*3+1],vertices[e.index1*3+2]);
                pointArray.push(vertices[e.index2*3],vertices[e.index2*3+1],vertices[e.index2*3+2]);
            }else{
                //e.face1.a ==>indices数组中顶点的索引
                //p1 ==> 顶点
                var p1={x:vertices[(e.face1.a)*3],y:vertices[(e.face1.a)*3+1],z:vertices[(e.face1.a)*3+2]};
                var p2={x:vertices[(e.face1.b)*3],y:vertices[(e.face1.b)*3+1],z:vertices[(e.face1.b)*3+2]};
                var p3={x:vertices[(e.face1.c)*3],y:vertices[(e.face1.c)*3+1],z:vertices[(e.face1.c)*3+2]};

                var q1={x:vertices[(e.face2.a)*3],y:vertices[(e.face2.a)*3+1],z:vertices[(e.face2.a)*3+2]};
                var q2={x:vertices[(e.face2.b)*3],y:vertices[(e.face2.b)*3+1],z:vertices[(e.face2.b)*3+2]};
                var q3={x:vertices[(e.face2.c)*3],y:vertices[(e.face2.c)*3+1],z:vertices[(e.face2.c)*3+2]};
                var face1Normal=getNormal(p1,p2,p3).elements;
                var face2Normal=getNormal(q1,q2,q3).elements;
                var dot=face1Normal[0]*face2Normal[0]+face1Normal[1]*face2Normal[1]+face1Normal[2]*face2Normal[2];
                if(dot<1){
                    pointArray.push(vertices[e.index1*3],vertices[e.index1*3+1],vertices[e.index1*3+2]);
                    pointArray.push(vertices[e.index2*3],vertices[e.index2*3+1],vertices[e.index2*3+2]);
                }

            }
        }
    }

    function getNormal(p1,p2,p3){
        var a = ( (p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y) );

        var b = ( (p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z) );

        var c = ( (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) );

        var normal=new Vector3([a,b,c])
        return normal.normalize();
    }

    function initVertexBuffers(gl,program) {
        // Create a cube
        //    v6----- v5
        //   /|      /|
        //  v1------v0|
        //  | |     | |
        //  | |v7---|-|v4
        //  |/      |/
        //  v2------v3
        // Coordinates
        // var cubeLength=Math.floor(Math.random()*2+2);
        var cubeLength=0.3;
        /*vertices = new Float32Array([
           2.0, 2.0, 2.0,  -2.0, 2.0, 2.0,  -2.0,-2.0, 2.0,   2.0,-2.0, 2.0, // v0-v1-v2-v3 front
           2.0, 2.0, 2.0,   2.0,-2.0, 2.0,   2.0,-2.0,-2.0,   2.0, 2.0,-2.0, // v0-v3-v4-v5 right
           2.0, 2.0, 2.0,   2.0, 2.0,-2.0,  -2.0, 2.0,-2.0,  -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
           -2.0, 2.0, 2.0,  -2.0, 2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
           -2.0,-2.0,-2.0,   2.0,-2.0,-2.0,   2.0,-2.0, 2.0,  -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
           2.0,-2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0, 2.0,-2.0,   2.0, 2.0,-2.0  // v4-v7-v6-v5 back
       ]);*/
        vertices = new Float32Array([
            cubeLength, cubeLength, cubeLength,  -cubeLength, cubeLength, cubeLength,  -cubeLength,-cubeLength, cubeLength,   cubeLength,-cubeLength, cubeLength, // v0-v1-v2-v3 front
            cubeLength, cubeLength, cubeLength,   cubeLength,-cubeLength, cubeLength,   cubeLength,-cubeLength,-cubeLength,   cubeLength, cubeLength,-cubeLength, // v0-v3-v4-v5 right
            cubeLength, cubeLength, cubeLength,   cubeLength, cubeLength,-cubeLength,  -cubeLength, cubeLength,-cubeLength,  -cubeLength, cubeLength, cubeLength, // v0-v5-v6-v1 up
            -cubeLength, cubeLength, cubeLength,  -cubeLength, cubeLength,-cubeLength,  -cubeLength,-cubeLength,-cubeLength,  -cubeLength,-cubeLength, cubeLength, // v1-v6-v7-v2 left
            -cubeLength,-cubeLength,-cubeLength,   cubeLength,-cubeLength,-cubeLength,   cubeLength,-cubeLength, cubeLength,  -cubeLength,-cubeLength, cubeLength, // v7-v4-v3-v2 down
            cubeLength,-cubeLength,-cubeLength,  -cubeLength,-cubeLength,-cubeLength,  -cubeLength, cubeLength,-cubeLength,   cubeLength, cubeLength,-cubeLength  // v4-v7-v6-v5 back
        ]);

        // Colors
        var colors = new Float32Array([
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0　    // v4-v7-v6-v5 back
        ]);

        // Normal
        normals = new Float32Array([
            0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
            1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
            0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
            -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
            0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
            0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
        ]);

        // Indices of the vertices
        indices = new Uint8Array([
            0, 1, 2,   0, 2, 3,    // front
            4, 5, 6,   4, 6, 7,    // right
            8, 9,10,   8,10,11,    // up
            12,13,14,  12,14,15,    // left
            16,17,18,  16,18,19,    // down
            20,21,22,  20,22,23     // back
        ]);

        // Write the vertex property to buffers (coordinates, colors and normals)
        if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT,program)) return -1;
        if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT,program)) return -1;
        if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT,program)) return -1;

        // Unbind the buffer object
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // Write the indices to the buffer object
        var indexBuffer = gl.createBuffer();
        if (!indexBuffer) {
            console.log('Failed to create the buffer object');
            return false;
        }
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

        return indices.length;
    }

    function initArrayBuffer(gl, attribute, data, num, type,program) {
        // Create a buffer object
        var buffer = gl.createBuffer();
        if (!buffer) {
            console.log('Failed to create the buffer object');
            return false;
        }
        // Write date into the buffer object
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
        // Assign the buffer object to the attribute variable
        var a_attribute = gl.getAttribLocation(program, attribute);
        if (a_attribute < 0) {
            console.log('Failed to get the storage location of ' + attribute);
            return false;
        }
        gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
        // Enable the assignment of the buffer object to the attribute variable
        gl.enableVertexAttribArray(a_attribute);

        return true;
    }




</script>
</html>
